#include <stdio.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <string.h>
#include <errno.h>

union semun {
	int              val;    /* 信号量的值 */
	struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
	unsigned short  *array;  /* Array for GETALL, SETALL */
	struct seminfo  *__buf;  /* Buffer for IPC_INFO
								(Linux-specific) */
};

// 将信号量下标为num的信号量做p操作
void sem_p(int semid, int num)
{
	struct sembuf action; // 定义信号量动作结构体变量
	action.sem_num = num; // 信号量下标num
	action.sem_op = -1; // p操作，减一操作
	action.sem_flg = 0; // 默认阻塞状态
	// 对信号量执行p操作
	semop(semid, &action, 1);
}

// 将信号量下标为num的信号量做v操作
void sem_v(int semid, int num)
{
	struct sembuf action; // 定义信号量动作结构体变量
	action.sem_num = num; // 信号量下标num
	action.sem_op = 1; // v操作，加一操作，不需要写+号
	action.sem_flg = 0; // 默认阻塞状态
	// 对信号量执行v操作
	semop(semid, &action, 1);
}

int main(int argc, char const *argv[])
{
	// 创建键值
	key_t shm_key = ftok(".", 1); // 共享内存键值
	if(shm_key == -1)
	{
		perror("shm_key ftok error\n");
		return -1;
	}

	key_t sem_key = ftok(".", 2); // 信号量键值
	if(sem_key == -1)
	{
		perror("sem_key ftok error\n");
		return -1;
	}

	// 获取共享内存的id号
	int shmid = shmget(shm_key, 1024, IPC_CREAT|IPC_EXCL|0666);
	if(shmid == -1)
	{
		if(errno == EEXIST)
		{
			shmid = shmget(shm_key, 1024, 0666);
			if(shmid == -1)
			{
				perror("shmget error\n");
				return -1;
			}
		}
	}
	// 映射共享内存到应用层
	char *shm_addr = shmat(shmid, NULL, 0);
	if(shm_addr == (void *)-1)
	{
		perror("shmat failed!\n");
		return -1;
	}
	// 创建信号量
	int semid = semget(sem_key, 2, IPC_CREAT|IPC_EXCL|0666);
	if(semid == -1)
	{
		if(errno == EEXIST)
		{
			semid = semget(sem_key, 2, 0666);
			if(semid == -1)
			{
				perror("semget error\n");
				return -1;
			}
		}
	}
	else if(semid > 0) // 设初值
	{
		union semun a;
		a.val = 1;
		semctl(semid, 0, SETVAL, a); // 下标0设初值为1

		a.val = 0;
		semctl(semid, 1, SETVAL, a); // 下标1设初值为0
	}

	while(1)
	{
		// 向共享内存写入数据
		printf("准备输入\n");

		sem_p(semid, 0);

		memset(shm_addr, 0, 1024);
		scanf("%s", shm_addr);
		printf("输入完成\n");

		sem_v(semid, 1); // 唤醒对方去读取数据
	}
	
	return 0;
}
